﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Media;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Configuration;
using System.Threading;

namespace Jerry.DroidBuilder.Common
{
    public static class Util
    {
        /// <summary>
        /// Initializes a new instance of <see cref="Util"/>.
        /// </summary>
        static Util()
        {
            ApplicationDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            RomsDirectory = GetConfiguredDirectory("ROMS_DIR", "roms", true);
            TempDirectory = GetConfiguredDirectory("TEMP_DIR", "temp", false);
            ToolsDirectory = GetConfiguredDirectory("TOOLS_DIR", "tools", true);
            PatchDirectory = GetConfiguredDirectory("PATCH_DIR", "patches", true);
            PluginDirectory = GetConfiguredDirectory("PLUGIN_DIR", "plugins", false);
        }

        public static string ApplicationDirectory { get; private set; }

        public static string RomsDirectory { get; private set; }

        public static string ToolsDirectory { get; private set; }

        public static string TempDirectory { get; private set; }

        public static string PatchDirectory { get; private set; }

        public static string PluginDirectory { get; private set; }

        public static void ShowMessage(string message)
        {
            Application.Current.Dispatcher.Invoke((ThreadStart)delegate
            {
                MessageBox.Show(Application.Current.MainWindow, message);
            });
        }

        /// <summary>
        /// Perform a deep Copy of the object.
        /// </summary>
        /// <typeparam name="T">The type of object being copied.</typeparam>
        /// <param name="source">The object instance to copy.</param>
        /// <returns>The copied object.</returns>
        public static T Clone<T>(T source)
        {
            if (!typeof(T).IsSerializable)
            {
                throw new ArgumentException("The type must be serializable.", "source");
            }

            if (Object.ReferenceEquals(source, null))
            {
                return default(T);
            }

            BinaryFormatter formatter = new BinaryFormatter();

            using (MemoryStream stream = new MemoryStream())
            {
                formatter.Serialize(stream, source);

                stream.Seek(0, SeekOrigin.Begin);

                return (T)formatter.Deserialize(stream);
            }
        }

        #region Load & Save Xml using serialization.

        public static TARGETTYPE LoadXml<TARGETTYPE>(string fileName) where TARGETTYPE : class
        {
            TARGETTYPE instance = null;
            XmlSerializer serializer = new XmlSerializer(typeof(TARGETTYPE));

            if (!File.Exists(fileName))
            {
                return instance;
            }

            using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                instance = serializer.Deserialize(stream) as TARGETTYPE;
            }

            return instance;
        }

        public static void SaveXml(string fileName, object instance)
        {
            XmlSerializer serializer = new XmlSerializer(instance.GetType());
            FileMode fileMode = File.Exists(fileName) ? FileMode.Truncate : FileMode.Create;

            using (FileStream stream = new FileStream(fileName, fileMode))
            {
                serializer.Serialize(stream, instance);
            }
        }

        #endregion

        /// <summary>
        /// Find a specified type of parent of current object.
        /// </summary>
        /// <param name="depObj">
        /// A instance of DependencyObject which is beginning.
        /// </param>
        public static TARGETTYPE FindParent<TARGETTYPE>(DependencyObject source) where TARGETTYPE : class
        {
            DependencyObject element = source;

            while (null != element && (typeof(TARGETTYPE) != element.GetType() || element == source))
            {
                DependencyObject current = element;

                if (element is Visual)
                {
                    element = VisualTreeHelper.GetParent(current);
                }

                if (null == element)
                {
                    element = LogicalTreeHelper.GetParent(current);
                }
            }

            return element as TARGETTYPE;
        }

        private static string GetConfiguredDirectory(string settingsKey, string defaultValue, bool checkExists)
        {
            var directory = string.Empty;
            var configed = ConfigurationManager.AppSettings[settingsKey];

            if (!string.IsNullOrEmpty(configed)
                && (!checkExists || Directory.Exists(configed)))
            {
                directory = configed;
            }
            else
            {
                directory = Path.Combine(ApplicationDirectory, defaultValue);
            }

            return new DirectoryInfo(directory).FullName;
        }
    }
}
